/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2009，所有权利保留。
 * 
 * 项目名：	rumba-widget2
 * 文件名：	RSplitButton.java
 * 模块说明：	
 * 修改历史：
 * Jan 18, 2009 - lxm - 创建。
 */
package com.hd123.rumba.gwt.widget2.client.button;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasAllFocusHandlers;
import com.google.gwt.event.dom.client.HasAllKeyHandlers;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FocusListener;
import com.google.gwt.user.client.ui.FocusPanel;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasFocus;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.ListenerWrapper;
import com.google.gwt.user.client.ui.SourcesClickEvents;
import com.hd123.rumba.gwt.widget2.client.action.IsRActionControl;
import com.hd123.rumba.gwt.widget2.client.action.RAction;
import com.hd123.rumba.gwt.widget2.client.hotkey.HasRHotKey;
import com.hd123.rumba.gwt.widget2.client.hotkey.RHotKey;
import com.hd123.rumba.gwt.widget2.client.hotkey.RHotKeyManager;
import com.hd123.rumba.gwt.widget2.client.menu.RPopupMenu;

/**
 * 
 * 分裂式按钮，可以关联一个下拉式菜单，并且具有不同按钮的功能。
 * <p>
 * 
 * <b>CSS Style Rules</b> <ol type=disc>
 * <li>.rb-SplitButton { 整个按钮 }</li>
 * <li>.rb-SplitButton-hovering { 按钮，当光标位于其上方时 }</li>
 * <li>.rb-SplitButton-disabled { 按钮，不可用状态 }</li>
 * <li>.rb-SplitButton .button { 按钮的左边按钮部分 }</li>
 * <li>.rb-SplitButton .icon { 按钮的图标部分 }</li>
 * <li>.rb-SplitButton .caption { 按钮的标题部分 }</li>
 * <li>.rb-SplitButton .arrow { 按钮的箭头部分 }</li> </ol>
 * 
 * @author lxm
 * 
 */
@SuppressWarnings("deprecation")
public class RSplitButton extends Composite implements IsRActionControl, HasHTML, HasFocus,
    Focusable, HasAllFocusHandlers, SourcesClickEvents, HasClickHandlers, HasAllKeyHandlers,
    HasRHotKey {

  private static final String STYLENAME_DEFAULT = "rb-SplitButton";

  /**
   * 创建对象实例。
   */
  public RSplitButton() {
    super();
    drawSelf();
  }

  /**
   * 创建对象实例。
   * 
   * @param action
   *          指定对应动作控件对象。
   */
  public RSplitButton(RAction action) {
    this();
    setAction(action);
  }

  public RSplitButton(String text) {
    this();
    setText(text);
  }

  /**
   * @deprecated 被{@link #RSplitButton(String, ClickHandler)}替代。
   */
  public RSplitButton(String text, ClickListener listener) {
    this(text);
    addClickListener(listener);
  }

  public RSplitButton(String text, ClickHandler handler) {
    this(text);
    addClickHandler(handler);
  }

  /**
   * @deprecated 被
   *             {@link #RSplitButton(String, RPopupMenu, ClickHandler)}
   *             替代。
   */
  public RSplitButton(String text, RPopupMenu menu, ClickListener listener) {
    this(text, listener);
    setMenu(menu);
  }

  public RSplitButton(String text, RPopupMenu menu, ClickHandler handler) {
    this(text, handler);
    setMenu(menu);
  }

  private RPopupMenu menu;
  private Image icon;
  private Image hoveringIcon;
  private Image disabledIcon;
  private boolean menuOnTop = false;
  private boolean enabled = true;
  private boolean hovering = false;
  private RAction action;
  private Grid grid;
  private FocusPanel pnlButton;
  private Grid gridButton;
  private FocusPanel pnlArrow;
  private HTML htmlCaption;
  private String title;
  private RHotKey hotKey;

  public String getHTML() {
    return htmlCaption.getHTML();
  }

  public void setHTML(String html) {
    htmlCaption.setHTML(html);
  }

  public String getText() {
    return htmlCaption.getText();
  }

  public void setText(String text) {
    htmlCaption.setText(text);
  }

  public int getTabIndex() {
    return pnlButton.getTabIndex();
  }

  public void setTabIndex(int index) {
    pnlButton.setTabIndex(index);
  }

  public void setAccessKey(char key) {
    pnlButton.setAccessKey(key);
  }

  public void setFocus(boolean focused) {
    pnlButton.setFocus(focused);
  }

  /**
   * @deprecated 被{@link #addFocusHandler(FocusHandler)}和
   *             {@link #addBlurHandler(BlurHandler)}替代。
   */
  public void addFocusListener(FocusListener listener) {
    ListenerWrapper.WrappedFocusListener.add(this, listener);
  }

  /**
   * @deprecated 被由{@link #addFocusHandler(FocusHandler)}和
   *             {@link #addBlurHandler(BlurHandler)} 返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeFocusListener(FocusListener listener) {
    ListenerWrapper.WrappedFocusListener.remove(this, listener);
  }

  public HandlerRegistration addFocusHandler(FocusHandler handler) {
    return addDomHandler(handler, FocusEvent.getType());
  }

  public HandlerRegistration addBlurHandler(BlurHandler handler) {
    return addDomHandler(handler, BlurEvent.getType());
  }

  /**
   * @deprecated 被{@link #addKeyDownHandler(KeyDownHandler)}、
   *             {@link #addKeyPressHandler(KeyPressHandler)} 和
   *             {@link #addKeyUpHandler(KeyUpHandler)}替代。
   */
  public void addKeyboardListener(KeyboardListener listener) {
    ListenerWrapper.WrappedKeyboardListener.add(this, listener);
  }

  /**
   * @deprecated 被由{@link #addKeyDownHandler(KeyDownHandler)}、
   *             {@link #addKeyPressHandler(KeyPressHandler)} 和
   *             {@link #addKeyUpHandler(KeyUpHandler)}所返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeKeyboardListener(KeyboardListener listener) {
    ListenerWrapper.WrappedKeyboardListener.remove(this, listener);
  }

  public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
    return addDomHandler(handler, KeyDownEvent.getType());
  }

  public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
    return addDomHandler(handler, KeyUpEvent.getType());
  }

  public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
    return addDomHandler(handler, KeyPressEvent.getType());
  }

  /**
   * @deprecated 被{@link #addClickHandler(ClickHandler)}替代。
   */
  public void addClickListener(ClickListener listener) {
    ListenerWrapper.WrappedClickListener.add(this, listener);
  }

  /**
   * @deprecated 被由{@link #addClickHandler(ClickHandler)}所返回的
   *             {@link HandlerRegistration#removeHandler()}替代。
   */
  public void removeClickListener(ClickListener listener) {
    ListenerWrapper.WrappedClickListener.remove(this, listener);
  }

  public HandlerRegistration addClickHandler(ClickHandler handler) {
    return addDomHandler(handler, ClickEvent.getType());
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
    refreshTitle();
  }

  /**
   * 根据title和热键刷新提示信息
   * 
   */
  protected void refreshTitle() {
    StringBuffer s = new StringBuffer();
    if (title != null)
      s.append(title);

    RHotKey hKey = null;
    if (hotKey != null)
      hKey = hotKey;
    else if (action != null)
      hKey = action.getHotKey();
    if (hKey != null) {
      if (s.length() > 0)
        s.append(" ");
      s.append(hKey.toString());
    }

    super.setTitle(s.toString());
  }

  public RAction getAction() {
    return action;
  }

  public void setAction(RAction action) {
    if (action == null) {
      if (this.action != null)
        this.action.removeControl(this);
    }

    this.action = action;

    if (this.action != null) {
      if (this.action.containsControl(this))
        return;
      this.action.addControl(this);
    }
  }

  public void applyAction() {
    if (action == null)
      return;
    setHTML(action.getCaption());
    setEnabled(action.isEnabled());
    setTitle(action.getTitle());
    if (action.getIcon() == null)
      setIcon(null);
    else
      setIcon(action.getIcon().createImage());
    if (action.getHoveringIcon() == null)
      setHoveringIcon(null);
    else
      setHoveringIcon(action.getHoveringIcon().createImage());
    if (action.getDisabledIcon() == null)
      setDisabledIcon(null);
    else
      setDisabledIcon(action.getDisabledIcon().createImage());
  }

  /** 按钮被按下后弹出的菜单 */
  public RPopupMenu getMenu() {
    return menu;
  }

  public void setMenu(RPopupMenu menu) {
    this.menu = menu;
  }

  /** 正常时候的图标。 */
  public Image getIcon() {
    return icon;
  }

  public void setIcon(Image icon) {
    this.icon = icon;
    refreshIcon();
  }

  /** 当光标位于按钮之上时显示的图标。null表示采用icon。 */
  public Image getHoveringIcon() {
    return hoveringIcon;
  }

  public void setHoveringIcon(Image hoveringIcon) {
    this.hoveringIcon = hoveringIcon;
    refreshIcon();
  }

  /** 当按钮处于不可用时显示的图标。null表示这时显示icon。 */
  public Image getDisabledIcon() {
    return disabledIcon;
  }

  public void setDisabledIcon(Image disabledIcon) {
    this.disabledIcon = disabledIcon;
    refreshIcon();
  }

  /** 是否将按钮显示在按钮上边。默认为false，即显示在按钮的下边。 */
  public boolean isMenuOnTop() {
    return menuOnTop;
  }

  public void setMenuOnTop(boolean menuOnTop) {
    this.menuOnTop = menuOnTop;
  }

  /** 是否可用 */
  public boolean isEnabled() {
    return enabled;
  }

  public void setEnabled(boolean enabled) {
    this.enabled = enabled;
    if (this.enabled) {
      removeStyleDependentName("disabled");
      DOM.removeElementAttribute(htmlCaption.getElement(), "disabled");
    } else {
      addStyleDependentName("disabled");
      DOM.setElementAttribute(htmlCaption.getElement(), "disabled", "true");
    }
    DOM.setElementPropertyBoolean(getElement(), "disabled", !this.enabled);
    refreshIcon();
  }

  public RHotKey getHotKey() {
    return hotKey;
  }

  public void setHotKey(RHotKey hotKey) {
    if (this.hotKey == hotKey)
      return;

    this.hotKey = hotKey;
    RHotKeyManager.remove(this);
    if (this.hotKey != null) {
      RHotKeyManager.add(this.hotKey, this, new Command() {
        public void execute() {
          if (enabled) {
            doButtonClick();
            DomEvent.fireNativeEvent(Document.get().createClickEvent(0, 0, 0, 0, 0, false, false,
                false, false), RSplitButton.this);
          }
        }
      });
    }

    refreshTitle();
  }

  public void clearHotKey() {
    setHotKey(null);
  }

  private void setHovering(boolean hovering) {
    this.hovering = hovering;
    if (enabled) {
      if (this.hovering)
        addStyleDependentName("hovering");
      else
        removeStyleDependentName("hovering");
    }
    refreshIcon();
  }

  private void drawSelf() {
    grid = new Grid(1, 2);
    grid.setCellPadding(0);
    grid.setCellSpacing(0);

    pnlButton = new FocusPanel();
    pnlButton.setTabIndex(0);
    Handler_pnlButton buttonHandler = new Handler_pnlButton();
    pnlButton.addClickHandler(buttonHandler);
    pnlButton.addMouseOverHandler(buttonHandler);
    pnlButton.addMouseOutHandler(buttonHandler);
    pnlButton.addKeyDownHandler(buttonHandler);
    pnlButton.addKeyPressHandler(buttonHandler);
    pnlButton.setStyleName("button");

    gridButton = new Grid(1, 2);
    gridButton.setCellPadding(0);
    gridButton.setCellSpacing(0);

    gridButton.setWidget(0, 0, new HTML());
    gridButton.getCellFormatter().setStyleName(0, 0, "icon");

    htmlCaption = new HTML();
    gridButton.setWidget(0, 1, htmlCaption);
    gridButton.getCellFormatter().setStyleName(0, 1, "caption");

    pnlButton.setWidget(gridButton);

    grid.setWidget(0, 0, pnlButton);

    pnlArrow = new FocusPanel();
    pnlArrow.setTabIndex(-1);
    Handler_pnlArrow arrowHandler = new Handler_pnlArrow();
    pnlArrow.addClickHandler(arrowHandler);
    pnlArrow.addMouseOverHandler(arrowHandler);
    pnlArrow.addMouseOutHandler(arrowHandler);
    pnlArrow.setStyleName("arrow");
    grid.setWidget(0, 1, pnlArrow);
    // grid.getCellFormatter().setStyleName(0, 1, "arrow");

    initWidget(grid);
    setStyleName(STYLENAME_DEFAULT);
    refreshIcon();
  }

  private void refreshIcon() {
    Image eIcon = icon;
    Image hIcon = hoveringIcon == null ? eIcon : hoveringIcon;
    Image dIcon = disabledIcon == null ? eIcon : disabledIcon;
    if (enabled) {
      if (hovering)
        gridButton.setWidget(0, 0, hIcon);
      else
        gridButton.setWidget(0, 0, eIcon);
    } else
      gridButton.setWidget(0, 0, dIcon);
  }

  private void doButtonClick() {
    if (!enabled)
      return;
    if (action != null)
      action.fireClick();
  }

  private void doArrowClick() {
    if (!enabled)
      return;
    if (menu != null) {
      if (menuOnTop)
        menu.show(this, this, RPopupMenu.top);
      else
        menu.show(this, this, RPopupMenu.bottom);
    }
    pnlButton.setFocus(true);
  }

  private class Handler_pnlButton implements ClickHandler, MouseOverHandler, MouseOutHandler,
      KeyPressHandler, KeyDownHandler {

    public void onClick(ClickEvent event) {
      doButtonClick();
    }

    public void onMouseOver(MouseOverEvent event) {
      setHovering(true);
    }

    public void onMouseOut(MouseOutEvent event) {
      setHovering(false);
    }

    public void onKeyPress(KeyPressEvent event) {
      char keyCode = event.getCharCode();
      if (keyCode == ' ' || keyCode == KeyCodes.KEY_ENTER || keyCode == '\r') {
        doButtonClick();
        NativeEvent ne = Document.get().createClickEvent(1, 0, 0, 0, 0, false, false, false, false);
        DomEvent.fireNativeEvent(ne, RSplitButton.this);
      }
    }

    public void onKeyDown(KeyDownEvent event) {
      int keyCode = event.getNativeKeyCode();
      if (keyCode == KeyCodes.KEY_DOWN || keyCode == KeyCodes.KEY_RIGHT)
        doArrowClick();
      else if (keyCode == KeyCodes.KEY_ESCAPE) {
        if (menu.isShowing())
          menu.hide();
      }
    }

  }

  private class Handler_pnlArrow implements MouseOverHandler, MouseOutHandler, ClickHandler {

    public void onMouseOver(MouseOverEvent event) {
      setHovering(true);
    }

    public void onMouseOut(MouseOutEvent event) {
      setHovering(false);
    }

    public void onClick(ClickEvent event) {
      doArrowClick();
      event.stopPropagation();
    }

  }

}
